package utils

import (
	"errors"
	"fmt"
	"github.com/golang-jwt/jwt/v4"
	"time"
)

const (
	SUBJECT    = "music"
	APP_SECRET = "79e7c69681b8270162386e6daa53d1dc"
	EXPIRE     = 60 * 60 * time.Second
)

type UserClaims struct {
	ID           int    `json:"id"`
	NickName     string `json:"nickname"`
	UserName     string `json:"username"`
	Sex          int    `json:"sex"`
	Phone        string `json:"phone"`
	HeaderImgUrl string `json:"header_img_url"`
	jwt.RegisteredClaims
}

// struct
func GenerateUserJWT(id int, nickName string, userName string, sex int, phone string, headerImgUrl string) string {
	claims := UserClaims{
		id,
		nickName,
		userName,
		sex,
		phone,
		headerImgUrl,
		jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(EXPIRE)), //过期时间
			IssuedAt:  jwt.NewNumericDate(time.Now()),             //签发时间
			NotBefore: jwt.NewNumericDate(time.Now()),             //声明要求当前日期/时间必须大于或等于“nbf”索赔中列出的非之前日期/时间
			Subject:   SUBJECT,                                    //令牌主题
		},
	}
	// Create a new token object, specifying signing method and the claims
	// you would like it to contain.
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// Sign and get the complete encoded token as a string using the secret
	tokenString, err := token.SignedString([]byte(APP_SECRET))
	if err != nil {
		fmt.Println(err)
		panic("token生成异常")
	}
	return tokenString
}

func ParseUserJWT(tokenString string) jwt.Claims {
	token, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(APP_SECRET), nil
	})

	if err != nil {
		panic("token解析异常")
	}

	claims, ok := token.Claims.(*UserClaims)
	if !ok || !token.Valid {
		panic("token解析异常")
	}

	return claims
}

// map
func GenerateJWT(mp map[string]interface{}) string {
	c := jwt.MapClaims{}
	//拷贝
	for k, v := range mp {
		c[k] = v
	}
	//过期时间
	c["exp"] = time.Now().Add(EXPIRE).Unix()
	//签发时间
	c["iat"] = time.Now().Unix()
	//声明要求当前日期/时间必须大于或等于“nbf”索赔中列出的非之前日期/时间
	c["nbf"] = time.Now().Unix()
	//令牌主题
	c["sub"] = SUBJECT

	t := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
	t.Header["alg"] = "HS256" //签名算法
	t.Header["typ"] = "JWT"   //令牌类型

	token, err := t.SignedString([]byte(APP_SECRET))

	if err != nil {
		panic("token生成异常")
	}

	return token
}

func ParseJWT(tokenString string) jwt.Claims {
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		// Don't forget to validate the alg is what you expect:
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			err := fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
			panic(err)
		}

		// hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key")
		return []byte(APP_SECRET), nil
	})
	claims, ok := token.Claims.(jwt.MapClaims)
	if !ok || !token.Valid {
		fmt.Println(err)
		panic("token解析异常")
	}
	return claims
}

func CheckJWT(tokenString string) (isValid bool) {
	// Token from another example.  This token is expired
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		return []byte(APP_SECRET), nil
	})

	if token.Valid {
		//fmt.Println("You look nice today")
		return true
	} else if errors.Is(err, jwt.ErrTokenMalformed) {
		//fmt.Println("That's not even a token")
		panic("That's not even a token")
	} else if errors.Is(err, jwt.ErrTokenExpired) || errors.Is(err, jwt.ErrTokenNotValidYet) {
		// Token is either expired or not active yet
		//fmt.Println("Timing is everything")
		panic("Timing is everything")
	} else {
		//fmt.Println("Couldn't handle this token:", err)
		panic(map[string]error{"Couldn't handle this token:": err})
	}
	return false
}
